package com.love.marriage.cloud.controller;

import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import com.love.marriage.cloud.service.WechatService;
import com.love.marriage.cloud.service.WechatWithdrawService;
import com.love.marriage.cloud.vo.CheckVo;
import com.love.marriage.cloud.vo.RedPacketResultVo;
import com.love.marriage.cloud.vo.RedPacketVo;
import com.love.marriage.cloud.vo.WithdrawVo;
import com.love.marriage.common.interfaces.avoid.AvoidRepeatableCommit;
import com.love.marriage.common.interfaces.avoid.LogRestRequest;
import com.love.marriage.common.vo.Result;
import com.love.marriage.common.vo.ResultBoolean;
import com.love.marriage.config.WeChatConfig;
import com.love.marriage.orderInfo.service.PayInfoService;
import com.love.marriage.userCommissionWithdrawal.vo.ResultUserCommissionWithdrawalVo;
import com.love.marriage.utils.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.web.bind.annotation.*;
import org.xml.sax.SAXException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

@ApiSort(100)
@Tag(name = "微信相关接口", description = "微信相关接口")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/wechat")
@Slf4j
public class WeChatLoginController {

    private final WeChatConfig weChatConfig;

    private final WechatService wechatService;

    private final PayInfoService payInfoService;

    private final WechatWithdrawService wechatWithdrawService;

    @ApiOperationSupport(order = 10, author = "alex")
    @Operation(summary = "获取登录二维码地址(公众号)", description = "获取登录二维码地址(公众号)")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @GetMapping(value = "/qrCode")
    @Tag(name = "公共", description = "公共接口")
    public String getWeChatQRCode() throws UnsupportedEncodingException {
        String redirectUri = URLEncoder.encode(weChatConfig.getRedirectUri(), String.valueOf(StandardCharsets.UTF_8));
        return String.format(
                "%s?appid=%s&redirect_uri=%s&response_type=code&scope=scope&state=STATE#wechat_redirect",
                weChatConfig.getQrConnectUrl(), weChatConfig.getAppId(), redirectUri);
    }

    @ApiOperationSupport(order = 10, author = "alex")
    @Operation(summary = "统一支付接口", description = "统一支付接口")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @GetMapping(value = "/unifiedOrder")
    @Parameters({
            @Parameter(name = "userId", description = "用户id", required = true, schema = @Schema(implementation = Long.class, type = "Long", example = "1")),
            @Parameter(name = "amount", description = "金额（分）", required = true, schema = @Schema(implementation = Long.class, type = "Integer", example = "1")),
            @Parameter(name = "attach", description = "类型：（membership:会员，'payBalance:充值,'chat':聊天，'getPhone':'获取电话号码," +
                    "'create_dating':创建约会,'join_dating':参加约会,'join_event':参加活动）", required = true, schema = @Schema(implementation = String.class, type = "String", example = "1"))
    })
    public Result<WxPayUnifiedOrderV3Result.JsapiResult> unifiedOrder(@RequestParam(value = "userId") Long userId,
                                                                      @RequestParam(value = "amount") Integer amount,
                                                                      @RequestParam(value = "attach") String attach) throws WxPayException {
        return Result.success(wechatService.unifiedOrder(userId, amount, attach, null, null));
    }

    @ApiOperationSupport(order = 10, author = "alex")
    @Operation(summary = "微信支付充值回调", description = "微信支付充值回调")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @PostMapping(value = "/notify")
    @Tag(name = "公共", description = "公共接口")
    public Result<Object> notify(@RequestBody String xmlData) throws WxPayException {
        return Result.success(payInfoService.notify(xmlData));
    }

    @ApiOperationSupport(order = 10, author = "alex")
    @Operation(summary = "微信支付充值回调测试", description = "微信支付充值回调测试")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @Parameters({
            @Parameter(name = "orderId", description = "订单id", required = true, schema = @Schema(implementation = Long.class, type = "Long", example = "1")),
            @Parameter(name = "attach", description = "类型：（membership:会员，'payBalance:充值）", required = true, schema = @Schema(implementation = Long.class, type = "Long", example = "1")),
            @Parameter(name = "commission", description = "是否回佣：默认true", schema = @Schema(implementation = Boolean.class, type = "Boolean", example = "true"))
    })
    @PostMapping(value = "/notifyTest")
    public Result<Object> notify(@RequestParam(value = "orderId") String orderId,
                                 @RequestParam(value = "attach") String attach,
                                 @RequestParam(value = "commission", required = false, defaultValue = "true") Boolean commission) throws Exception {
        return Result.success(payInfoService.notify(orderId, attach, commission));
    }

    @ApiOperationSupport(order = 20, author = "alex")
    @Operation(summary = "发送消息", description = "发送消息")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @PostMapping(value = "/sentUserDetailMessage")
    public Result<Object> sentUserDetailMessage() throws WxPayException, WxErrorException {
        String openId = "oFuCI6ielw8VMmZWiHzWQkSD0UWE";
        Long userId = 1853029205036089345L;
        String userCode = "61525";
        String type = "detail";
        return Result.success(wechatService.sentUserMessage(openId, userId, userCode, type));
    }

    @ApiOperationSupport(order = 25, author = "alex")
    @Operation(summary = "发送订阅消息", description = "发送订阅消息")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @PostMapping(value = "/sentUserSubscribeMessage")
    public Result<Object> sentUserSubscribeMessage() throws WxPayException, WxErrorException {
        String openId = "oFuCI6ielw8VMmZWiHzWQkSD0UWE";
        Long userId = 1853029205036089345L;
        String userCode = "61525";
        String type = "detail";
        return Result.success(wechatService.sentUserSubscribeMessage(openId, userId, userCode, type));
    }

    @ApiOperationSupport(order = 30, author = "alex")
    @Operation(summary = "发送匹配消息", description = "发送匹配消息")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @PostMapping(value = "/sentUserPartnerMessage")
    public Result<Object> sentUserPartnerMessage() throws WxPayException, WxErrorException {
        return Result.success(wechatService.sentUserPartnerMessageInfo());
    }

    @ApiOperationSupport(order = 50, author = "alex")
    @Operation(summary = "公众号回调验证", description = "公众号回调验证")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @GetMapping(value = "gzhCallBack")
    public String verifyServer(@RequestParam("signature") String signature,
                               @RequestParam("timestamp") String timestamp,
                               @RequestParam("nonce") String nonce,
                               @RequestParam("echostr") String echostr) {
        // 在这里校验签名是否正确，如果正确则返回 `echostr`
        if (wechatService.checkSignature(signature, timestamp, nonce)) {
            return echostr;
        }
        return "error";
    }

    @ApiOperationSupport(order = 60, author = "alex")
    @Operation(summary = "公众号回调", description = "公众号回调")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @PostMapping(value = "/gzhCallBack")
    public Result<Boolean> gzhCallBack(HttpServletRequest request) throws WxPayException, WxErrorException, ParserConfigurationException, IOException, SAXException {
        return Result.success(wechatService.gzhCallBack(request));
    }

    @ApiOperationSupport(order = 70, author = "alex")
    @Operation(summary = "微信提现到微信余额（商户提现到余额）", description = "微信提现到微信余额（商户提现到余额）")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = Result.class
                    )))
    @PostMapping(value = "/withdrawToWeChat")
    public Result<Map<String, String>> withdrawToWeChat() throws Exception {
        String openId = "oNSWe7WVfFpH-CvzBrUg4sweHZvU";
        String outTradeNo = "dd12345";
        int amount = 1;
        String description = "测试提现";
        return Result.success(wechatWithdrawService.withdrawToWeChat(openId, outTradeNo, amount, description));
    }

    @ApiOperationSupport(order = 74, author = "alex")
    @LogRestRequest(apiName = "商户转账到微信余额")
    @Operation(summary = "商户转账到微信余额（前端）", description = "商户转账到微信余额（前端）",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "查询条件",
                    required = false,
                    content = @Content(schema = @Schema(implementation = WithdrawVo.class))
            ))
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = Result.class
                    )))
    @PostMapping(value = "/withdrawMoneyToWeChat")
    public Result<Map<String, String>> withdrawMoneyToWeChat(@RequestBody WithdrawVo withdrawVo) throws Exception {
        return Result.success(wechatService.withdrawMoneyToWeChat(withdrawVo));
    }

    @ApiOperationSupport(order = 75, author = "alex")
    @LogRestRequest(apiName = "商户转账到微信余额回调")
    @Operation(summary = "商户转账到微信余额回调（前端）", description = "商户转账到微信余额回调（前端）")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = Result.class
                    )))
    @PostMapping(value = "/withdrawToWeChat/notify")
    public Result<Boolean> withdrawToWeChatNotify(@RequestHeader("Wechatpay-Serial") String serial,
                                                  @RequestHeader("Wechatpay-Signature") String signature,
                                                  @RequestHeader("Wechatpay-Timestamp") String timestamp,
                                                  @RequestHeader("Wechatpay-Nonce") String nonce,
                                                  @RequestBody String requestBody) throws Exception {
        return Result.success(wechatWithdrawService.withdrawToWeChatNotify(serial, signature, timestamp, nonce, requestBody));
    }

    @AvoidRepeatableCommit
    @ApiOperationSupport(order = 70, author = "mybatisPlus")
    @Operation(summary = "取消微信提现", description = "取消微信提现")
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultUserCommissionWithdrawalVo.class
                    )))
    @Parameters({
            @Parameter(name = "id", description = "用户提现id", required = true, schema = @Schema(implementation = Long.class, type = "Long", example = "1")),
            @Parameter(name = "status", description = "状态(COMPLETED:完成,CANCEL:取消)", schema = @Schema(implementation = String.class, type = "String", example = "1"))
    })
    @GetMapping(value = "cancelWithdrawToWeChat")
    public Result<Boolean> cancelWithdrawToWeChat(@RequestParam(value = "id") Long id,
                                                  @RequestParam(value = "status", required = false) String status) throws WxPayException {
        if (StringUtils.isBlank(status)) {
            status = "COMPLETED";
        }
        return Result.success(wechatService.cancelWithdrawToWeChat(id, status));
    }

    @ApiOperationSupport(order = 80, author = "alex")
    @Operation(summary = "敏感信息校验", description = "敏感信息校验",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "查询条件",
                    required = false,
                    content = @Content(schema = @Schema(implementation = CheckVo.class))
            ))
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = ResultBoolean.class
                    )))
    @PostMapping(value = "/secCheck")
    public Result<CheckVo> secCheck(@RequestBody CheckVo checkVo) throws Exception {
        return Result.success(wechatService.secCheck(checkVo));
    }

    @GetMapping(value = "secCheckCallback")
    public String verifyURL(@RequestParam("signature") String signature,
                            @RequestParam("timestamp") String timestamp,
                            @RequestParam("nonce") String nonce,
                            @RequestParam("echostr") String echostr) {
        // 在这里校验签名是否正确，如果正确则返回 `echostr`
        if (wechatService.checkSignature(signature, timestamp, nonce)) {
            return echostr;
        }
        return "error";
    }

    @ApiOperationSupport(order = 90, author = "alex")
    @Operation(summary = "敏感信息校验回调", description = "敏感信息校验回调",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "查询条件",
                    required = false,
                    content = @Content(schema = @Schema(implementation = CheckVo.class))
            ))
    @PostMapping(value = "/secCheckCallback")
    public void secCheckCallback(@RequestBody String requestBody,
                                 @RequestParam("signature") String signature,
                                 @RequestParam("timestamp") String timestamp,
                                 @RequestParam("nonce") String nonce,
                                 @RequestParam(value = "encrypt_type", required = false) String encryptType,
                                 @RequestParam(value = "msg_signature", required = false) String msgSignature,
                                 HttpServletResponse response) throws Exception {
        wechatService.secCheckCallback(requestBody, signature, timestamp, nonce, encryptType, msgSignature, response);
    }

    @ApiOperationSupport(order = 100, author = "alex")
    @Operation(
            summary = "获取图片回调信息",
            description = "根据提供的 traceIds 获取图片回调信息",
            parameters = {
                    @Parameter(
                            name = "traceIds",
                            description = "跟踪ID列表",
                            required = true
                    )
            }
    )
    @GetMapping(value = "/getImgResult")
    public Result<Map<String, String>> getImgResult(@RequestParam(value = "traceIds") List<String> traceIds) {
        return Result.success(wechatService.getImgResult(traceIds));
    }

    @AvoidRepeatableCommit
    @ApiOperationSupport(order = 110, author = "alex")
    @LogRestRequest(apiName = "发送微信红包")
    @Operation(summary = "发送微信红包", description = "商户发送红包给微信用户",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "红包信息",
                    required = true,
                    content = @Content(schema = @Schema(implementation = RedPacketVo.class))
            ))
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = Result.class
                    )))
    @PostMapping(value = "/sendRedPacket")
    public Result<RedPacketResultVo> sendRedPacket(@RequestBody RedPacketVo redPacketVo) throws Exception {
        return Result.success(wechatService.sendRedPacket(redPacketVo));
    }

    @AvoidRepeatableCommit
    @ApiOperationSupport(order = 110, author = "alex")
    @LogRestRequest(apiName = "小程序发送微信红包")
    @Operation(summary = "小程序发送微信红包", description = "小程序发送微信红包",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "红包信息",
                    required = true,
                    content = @Content(schema = @Schema(implementation = RedPacketVo.class))
            ))
    @ApiResponse(responseCode = "200", description = "Success",
            content = @Content(mediaType = "application/json",
                    schema = @Schema(implementation = Result.class
                    )))
    @PostMapping(value = "/sendMinProgramRedPacket")
    public Result<RedPacketResultVo> sendMinProgramRedPacket(@RequestBody RedPacketVo redPacketVo) throws Exception {
        return Result.success(wechatService.sendMinProgramRedPacket(redPacketVo));
    }
}
